Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: shift from JsonString to JsonValue #5012

Closed
wants to merge 3 commits into from

Conversation

0x009922
Copy link
Contributor

Context

JsonString is a type in the schema for arbitrary JSON values. In SCALE it is encoded as a string (hence the name), while in JSON it is inlined as any JSON value.

There are a few issues:

  • Ambiguity of Option<JsonString> in JSON: it is impossible to distinguish between None and Some("null"), since both serialize as null in actual JSON.
  • Schema's purpose is solely to describe SCALE encoding and it doesn't tell anything about JsonString representation in JSON. In JSON it is not a string, which caused confusion for SDK developers.

This PR fixes #4900 by disallowing Option<JsonString> and essentially forcing SDKs to handle JSON values as a special case and account for its differences in SCALE vs JSON.

Solution

by these changes:

  • Rename JsonString to JsonValue and make stronger emphasis on using the native Rust JSON type, i.e. serde_json::Value. Remove ambiguous conversions and delegate it to the serde_json::Value itself.
  • Add struct JsonValueWrap { value: JsonValue } for use with Option
  • In schema, treat JsonValue as a special type, i.e. not as an alias to String, and forbid use of Option<JsonValue>.

Migration Guide

There is a breaking change for SDK developers. Apart from JsonString being renamed to JsonValue, it is also no longer an alias to String type, but instead is a special type JsonValue. SDK should treat it as a special case in a way that is suitable for their platform.

Binary and JSON serialisation compatibility is not broken. However, JSON form of ExecuteTrigger instruction and ExecuteTriggerEvent changed: its args field was an Option<JsonString> with ambiguous null value. Now it is Option<JsonValueWrap>, meaning that you need to use it this way:

// Before
{ "args": null }
{ "args": 42 }

// After
{ "args": null }
{ "args": { "value": null } }
{ "args": { "value": 42 } }

Review notes

  • Start from primitives/json.rs, then schema, then the rest
  • I am not sure about JsonValue method names, especially get(&self) -> &Value.

Checklist

  • I've read CONTRIBUTING.md.
  • (optional) I've written unit tests for the code changes.
  • All review comments have been resolved.

@0x009922 0x009922 added Bug Something isn't working api-changes Changes in the API for client libraries labels Aug 27, 2024
@0x009922 0x009922 self-assigned this Aug 27, 2024
@@ -930,7 +930,7 @@ mod transparent {
/// Id of a trigger to execute
pub trigger: TriggerId,
/// Arguments to trigger execution
pub args: Option<JsonString>,
pub args: Option<JsonValueWrap>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use just JsonValue? and absence of arguments could be just "null"

@@ -7,170 +7,281 @@ use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::str::FromStr;
// use core::str::FromStr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented code.

@@ -80,7 +80,8 @@ fn mutlisig() -> Result<()> {
.map(Register::account),
)?;

let call_trigger = ExecuteTrigger::new(multisig_register_trigger_id).with_args(&args);
let call_trigger = ExecuteTrigger::new(multisig_register_trigger_id)
.with_args(serde_json::to_value(&args).unwrap());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like a step back

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed implicit conversions on purpose.

Before there was From<T: Serialize> for JsonValue, and was able to panic implicitly if for some reason T fails to serialize as JSON. Now it is explicit, at least.

I tried to make emphasis on relying on the "standard" way of working with JSON in Rust, i.e. on serde_json.

Actually, this serde_json::to_value(&args).unwrap() could be shortcut to just json!(&args). And I would strongly prefer that rather than adding some methods to JsonValue, making it unintuitive to use.

@0x009922
Copy link
Contributor Author

0x009922 commented Sep 2, 2024

Temporarily suspended in favor of #5024

@0x009922 0x009922 added the blocked this problem can't be fixed yet label Sep 2, 2024
@0x009922 0x009922 closed this Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-changes Changes in the API for client libraries blocked this problem can't be fixed yet Bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

JsonString in schema vs in json
3 participants